package ckw.gui;

import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ckw.database.model.Election;
import ckw.database.model.ElectionNode;
import ckw.database.model.ElectionNonResidentVoter;
import ckw.database.model.ElectionResidentVoter;
import ckw.database.model.Voter;
import core.exceptions.CsvFormatException;
import core.utils.Converter;

public class WzdPgAddEditElectionRKWVoters extends ModelValidationWizardPage {
	private static final Logger LOGGER = LoggerFactory.getLogger(WzdPgAddEditElectionRKWVoters.class);

	private static class VoterContentProvider implements ITreeContentProvider {
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}

		@Override
		public void dispose() {
		}

		public Object[] getElements(Object inputElement) {
			return getChildren(inputElement);
		}

		@SuppressWarnings("unchecked")
		public Object[] getChildren(Object parentElement) {
			if (parentElement instanceof List) {
				List<Voter> voterList = (List<Voter>) parentElement;
				return voterList.toArray();
			}
			return new Object[] {};
		}

		@Override
		public Object getParent(Object element) {
			return null;
		}

		public boolean hasChildren(Object element) {
			return getChildren(element).length > 0;
		}
	}

	private static class VoterLabelProvider extends LabelProvider {
		public String getText(Object element) {
			if (element instanceof Voter) {
				Voter voter = (Voter) element;
				return voter == null ? "" : voter.getSurname() + ", " + voter.getName() + " - " + voter.getIdentificationField() + ", " + voter.getNode().getName();
			}
			return super.getText(element);
		}
	}

	private static class AddedVoterTreeLabelProvider extends LabelProvider {
		public Image getImage(Object element) {
			return super.getImage(element);
		}

		public String getText(Object element) {
			if (element instanceof ElectionNonResidentVoter[]) {
				return "Non resident Voters";
			}
			if (element instanceof ElectionNonResidentVoter) {
				return element == null ? "" : ((ElectionNonResidentVoter) element).getVoter().getSurname() + ", " + ((ElectionNonResidentVoter) element).getVoter().getName();
			}
			if (element instanceof ElectionNode) {
				return element == null ? "" : ((ElectionNode) element).getNode().getName();
			}
			if (element instanceof ElectionResidentVoter) {
				return element == null ? "" : ((ElectionResidentVoter) element).getVoter().getSurname() + ", " + ((ElectionResidentVoter) element).getVoter().getName();
			}
			return super.getText(element);
		}
	}

	private static class AddedVoterTreeContentProvider implements ITreeContentProvider {
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}

		public void dispose() {
		}

		public Object[] getElements(Object inputElement) {
			return getChildren(inputElement);
		}

		public Object[] getChildren(Object parentElement) {
			if (parentElement instanceof Election) {
				Election election = (Election) parentElement;
				ElectionNonResidentVoter[] electionNonResidentVoters = election.getVoters().toArray(new ElectionNonResidentVoter[election.getVoters().size()]);
				List<Object> childrenList = new ArrayList<Object>();
				childrenList.add(electionNonResidentVoters);
				for (ElectionNode electionNode : election.getNodesAsSortedList()) {
					childrenList.add(electionNode);
				}
				return childrenList.toArray();
			}
			if (parentElement instanceof ElectionNonResidentVoter[]) {
				return (Object[]) parentElement;
			}
			if (parentElement instanceof ElectionNode) {
				ElectionNode electionNode = (ElectionNode) parentElement;
				return electionNode.getVoters().toArray();
			}
			return new Object[] {};
		}

		public Object getParent(Object element) {
			return null;
		}

		public boolean hasChildren(Object element) {
			return getChildren(element).length > 0;
		}
	}

	private boolean addElection;
	private Election election;

	private TreeViewer trVwrAddedVoters;
	private Tree trAddedVoters;
	private TreeViewer trVwrVoters;
	private Tree trVoters;
	private Text txtSearch;
	private Composite cmpButtons;
	private Button btnAddAsResident;
	private Button btnAddAsNonresident;
	private Button btnDelete;
	private Button btnImport;

	/**
	 * Create the wizard.
	 */
	public WzdPgAddEditElectionRKWVoters(boolean addElection, Election election) {
		super("WzdPgAddEditElectionVoters");

		this.addElection = addElection;
		this.election = election;

		if (addElection) {
			setTitle("Add Elections Voters");
		} else {
			setTitle("Edit Elections Voters");
		}

		setDescription("Set Elections Voters");
	}

	/**
	 * Create contents of the wizard.
	 * 
	 * @param parent
	 */
	public void createControl(Composite parent) {
		Composite cmpMain = new Composite(parent, SWT.NULL);

		setControl(cmpMain);
		cmpMain.setLayout(new GridLayout(3, false));

		Label lblVoters = new Label(cmpMain, SWT.NONE);
		lblVoters.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 3, 1));
		lblVoters.setText("Voters");

		trVwrAddedVoters = new TreeViewer(cmpMain, SWT.BORDER | SWT.MULTI);
		trAddedVoters = trVwrAddedVoters.getTree();
		trAddedVoters.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				List<TreeItem> selectionList = new ArrayList<TreeItem>();
				for (TreeItem treeItem : trAddedVoters.getSelection()) {
					if (treeItem.getData() instanceof ElectionNonResidentVoter || treeItem.getData() instanceof ElectionResidentVoter) {
						selectionList.add(treeItem);
					}
				}
				trAddedVoters.setSelection(selectionList.toArray(new TreeItem[selectionList.size()]));
			}
		});
		trAddedVoters.setLinesVisible(true);
		trAddedVoters.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2));
		trVwrAddedVoters.setLabelProvider(new AddedVoterTreeLabelProvider());
		trVwrAddedVoters.setContentProvider(new AddedVoterTreeContentProvider());

		txtSearch = new Text(cmpMain, SWT.BORDER);
		txtSearch.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent event) {
				trVwrVoters.setInput(manager.findVotersLike(txtSearch.getText().trim()));
			}
		});
		txtSearch.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		txtSearch.setMessage("Type search phrase");

		cmpButtons = new Composite(cmpMain, SWT.NONE);
		cmpButtons.setLayout(new FillLayout(SWT.VERTICAL));
		GridData gd_cmpButtons = new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 2);
		gd_cmpButtons.widthHint = 150;
		cmpButtons.setLayoutData(gd_cmpButtons);

		btnAddAsResident = new Button(cmpButtons, SWT.NONE);
		btnAddAsResident.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				for (TreeItem selectedTreeItem : trVoters.getSelection()) {
					manager.addResidentVoter(election, (Voter) selectedTreeItem.getData());
				}
				refreshPage();
			}
		});
		btnAddAsResident.setText("Add as Resident");

		btnAddAsNonresident = new Button(cmpButtons, SWT.NONE);
		btnAddAsNonresident.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				for (TreeItem selectedTreeItem : trVoters.getSelection()) {
					manager.addNonResidentVoter(election, (Voter) selectedTreeItem.getData());
				}
				refreshPage();
			}
		});
		btnAddAsNonresident.setText("Add as Nonresident");

		btnDelete = new Button(cmpButtons, SWT.NONE);
		btnDelete.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				for (TreeItem selectedTreeItem : trAddedVoters.getSelection()) {
					if (selectedTreeItem.getData() instanceof ElectionNonResidentVoter) {
						ElectionNonResidentVoter electionNonResidentVoter = (ElectionNonResidentVoter) selectedTreeItem.getData();
						manager.removeNonResidentVoter(electionNonResidentVoter);
						continue;
					}
					if (selectedTreeItem.getData() instanceof ElectionResidentVoter) {
						ElectionResidentVoter electionResidentVoter = (ElectionResidentVoter) selectedTreeItem.getData();
						manager.removeResidentVoter(electionResidentVoter);
						continue;
					}
				}
				refreshPage();
			}
		});
		btnDelete.setText("Delete");

		btnImport = new Button(cmpButtons, SWT.NONE);
		btnImport.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				FileDialog fileDialog = new FileDialog(getShell());
				fileDialog.setFilterNames(new String[] { "CSV Files" });
				fileDialog.setFilterExtensions(new String[] { "*.csv" });
				// TODO zrobić path do domyślnego folderu z csv'kami - w jakimś propertiesie może?
				// fileDialog.setFilterPath(string);

				String filePath = fileDialog.open();
				if (filePath != null) {
					CSVParser parser = null;
					try {
						parser = new CSVParser(new FileReader(filePath), Converter.getCSVFormat());
						List<String> residentVoterIdetificationFields = new ArrayList<String>();
						List<String> nonResidentVoterIdetificationFields = new ArrayList<String>();
						String wrongCSVFormatMsg = "Wrong CSV format. Correct format per line: \nidentification-field;r - for resident voter\nidentification-field;n - for non-resident voter";
						for (CSVRecord csvRecord : parser.getRecords()) {
							if (csvRecord.size() != 2) {
								throw new CsvFormatException(wrongCSVFormatMsg);
							}
							String voterIdentificationField = csvRecord.get(0);
							String voterType = csvRecord.get(1);
							if (voterType.compareTo("r") == 0) {
								residentVoterIdetificationFields.add(voterIdentificationField);
							} else if (voterType.compareTo("n") == 0) {
								nonResidentVoterIdetificationFields.add(voterIdentificationField);
							} else {
								throw new CsvFormatException(wrongCSVFormatMsg);
							}
						}
						Voter voter;
						for (String residentVoterIdetificationField : residentVoterIdetificationFields) {
							voter = manager.getVoterByIdentificationField(residentVoterIdetificationField);
							manager.addResidentVoter(election, voter);
						}
						for (String nonResidentVoterIdetificationField : nonResidentVoterIdetificationFields) {
							voter = manager.getVoterByIdentificationField(nonResidentVoterIdetificationField);
							manager.addNonResidentVoter(election, voter);
						}
						refreshPage();
					} catch (IOException e) {
						LOGGER.error(e.getMessage());
					} catch (CsvFormatException e) {
						MessageDialog.openError(getShell(), "Error", e.getMessage());
					} finally {
						if (parser != null) {
							try {
								parser.close();
							} catch (IOException e) {
								LOGGER.error(e.getMessage());
							}
						}
					}
				}
			}
		});
		btnImport.setText("Import");

		trVwrVoters = new TreeViewer(cmpMain, SWT.BORDER | SWT.MULTI);
		trVoters = trVwrVoters.getTree();
		trVoters.setLinesVisible(true);
		trVoters.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
		trVwrVoters.setContentProvider(new VoterContentProvider());
		trVwrVoters.setLabelProvider(new VoterLabelProvider());

		initValues();
	}

	private void initValues() {
		trVwrAddedVoters.setInput(election);
		trVwrVoters.setInput(manager.getAllVoters());
		trVwrAddedVoters.expandAll();
		if (addElection) {
			setPageComplete(false);
		}
	}

	@Override
	protected void onEnterPage() {
		refreshPage();
	}

	protected void refreshPage() {
		trVwrAddedVoters.refresh();
		trVwrAddedVoters.expandAll();

		validate();
	}

	@Override
	protected void validate() {
		clearErrorState();
	}

	@Override
	protected void update() {

	}
}
